/************************************************
 * Author       :   XXY
 * Blog         :   https://www.cnblogs.com/jzyl
 * Gitee        :   https://gitee.com/xiaobaichong
 * Email        :   1047185209@QQ.com
 * FileName     :   MediaCanvas.cs
 * CreateData   :   2023/11/11 10:52:09
 * UnityVersion :   2021.3.30f1c1
 * Description  :   媒体画板-》包括视频，图片，文字，声音，模型
************************************************/

using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.Video;
using static UnityEngine.GraphicsBuffer;

namespace Main.Framework
{
    public class MediaCanvas : MonoBehaviour
    {
        #region 组件
        [SerializeField] Transform videoContentIDE;
        [SerializeField] Transform imageContentIDE;
        [SerializeField] Transform audioContentIDE;
        [SerializeField] Transform modelContentIDE;
        [SerializeField] Transform textContentIDE;
        #endregion

        #region 属性
        public TextContent TextContent => textContent;
        public ImageContent ImageContent => imageContent;
        public VideoContent VideoContent => videoContent;
        public AudioContent AudioContent => audioContent;
        public ModelContent ModelContent => modelContent;
        #endregion

        TextContent textContent;
        ImageContent imageContent;
        VideoContent videoContent;
        AudioContent audioContent;
        ModelContent modelContent;
        private void Awake()
        {
            textContent = new TextContent(textContentIDE);
            imageContent = new ImageContent(imageContentIDE);
            videoContent = new VideoContent(videoContentIDE);
            audioContent = new AudioContent(audioContentIDE);
            modelContent = new ModelContent(modelContentIDE);
        }

        private void Update()
        {
            if (textContentIDE.gameObject.activeSelf) textContent.Update();
            if (imageContentIDE.gameObject.activeSelf) imageContent.Update();
            if (videoContentIDE.gameObject.activeSelf) videoContent.Update();
            if (audioContentIDE.gameObject.activeSelf) audioContent.Update();
            if (modelContentIDE.gameObject.activeSelf) modelContent.Update();
        }
    }



    /// <summary>
    /// 媒体窗口基类（标题，关闭，最大化/最小化）
    /// </summary>
    public class MediaWindow
    {
        protected RectTransform winRect;        // 页面窗口
        protected RectTransform topBar;          // 顶部区域
        protected RectTransform mainContent;   // 中部区域
        protected RectTransform downBar;         // 底部区域


        protected Text title;               // 标题
        protected Button closeBtn;          // 关闭按钮
        protected ButtonToggle maxToggle;   // 最大最小化
        public MediaWindow(Transform transform)
        {
            this.winRect = (RectTransform)transform;
            // 初始化字段
            topBar = (RectTransform)winRect.Find("TopBar");
            mainContent = (RectTransform)winRect.Find("MainContent");
            downBar = (RectTransform)winRect.Find("DownBar");

            title = topBar.Find("Title").GetComponent<Text>();
            closeBtn = topBar.Find("CloseBtn").GetComponent<Button>();
            maxToggle = downBar.Find("MaxToggle").GetComponent<ButtonToggle>();
            // 事件绑定
            closeBtn.onClick.AddListener(OnClose);
            maxToggle.onClick.AddListener(OnToggleSize);

            EventTrigger trigger;
            if (!topBar.TryGetComponent<EventTrigger>(out trigger))
            {
                trigger = topBar.gameObject.AddComponent<EventTrigger>();
            }
            EventTrigger.Entry begin = new EventTrigger.Entry();
            begin.eventID = EventTriggerType.BeginDrag;
            begin.callback.AddListener(OnBeginDrag);
            trigger.triggers.Add(begin);

            EventTrigger.Entry dragging = new EventTrigger.Entry();
            dragging.eventID = EventTriggerType.Drag;
            dragging.callback.AddListener(OnDrag);
            trigger.triggers.Add(dragging);
        }
        protected Action closeAction;

        protected void OnClose()
        {
            closeAction?.Invoke();
            closeAction = null;
            winRect.gameObject.SetActive(false);
        }
        protected virtual void OnToggleSize(bool arg)
        {
            if (maxToggle.IsOn)
            {
                winRect.anchorMin = Vector2.zero;
                winRect.anchorMax = Vector2.one;
                winRect.offsetMax = Vector2.up * 60;
            }
            else
            {
                winRect.anchorMin = Vector2.one * 0.2f;
                winRect.anchorMax = Vector2.one * 0.8f;
                winRect.offsetMax = Vector2.zero;
            }
            winRect.offsetMin = Vector2.zero;
        }
        Vector2 offsetPos;
        private void OnBeginDrag(BaseEventData arg)
        {
            PointerEventData pointer = arg as PointerEventData;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)winRect.parent, pointer.position, null, out Vector2 pos))
            {
                offsetPos = pos - winRect.anchoredPosition;
            }
        }
        private void OnDrag(BaseEventData arg)
        {
            PointerEventData pointer = arg as PointerEventData;
            RectTransform screen = (RectTransform)winRect.parent;
            if (RectTransformUtility.ScreenPointToLocalPointInRectangle(screen, pointer.position, null, out Vector2 pos))
            {
                pos -= offsetPos;
                pos.x = Mathf.Clamp(pos.x, (-screen.rect.width + winRect.rect.width) / 2, (screen.rect.width - winRect.rect.width) / 2);
                pos.y = Mathf.Clamp(pos.y, (-screen.rect.height + winRect.rect.height) / 2, (screen.rect.height - winRect.rect.height) / 2);
                winRect.anchoredPosition = pos;
            }
        }
        private float doubleClickTime;
        public virtual void Update()
        {
            // 点击主区域
            if (Input.GetMouseButtonDown(0) || (Input.touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Began))
            {
                doubleClickTime += 0.5f;
                Vector2 pos = Input.mousePosition;
                if (Input.touchCount > 0)
                {
                    pos = Input.GetTouch(0).position;
                }
                if (doubleClickTime > 0.8f && RectTransformUtility.RectangleContainsScreenPoint(mainContent, pos, null, new Vector4(0, 60, 0, 0)))
                {
                    // 切换显示底部栏
                    doubleClickTime = 0;
                    downBar.gameObject.SetActive(!downBar.gameObject.activeSelf);
                }
            }
            doubleClickTime -= Time.deltaTime;
            doubleClickTime = Mathf.Clamp01(doubleClickTime);
        }
    }

    public class TextContent : MediaWindow
    {
        private Text contentTxt;

        public TextContent(Transform transform) : base(transform)
        {
            contentTxt = mainContent.Find("Viewport/Content").GetComponent<Text>();
        }
        public override void Update()
        {
            base.Update();
        }
        /// <summary>
        /// 打开窗口显示文本内容
        /// </summary>
        /// <param name="msg">要显示的内容</param>
        /// <param name="closeAction">关闭回调</param>
        public void Open(string msg, string title = "", Action closeAction = null)
        {
            winRect.gameObject.SetActive(true);
            winRect.anchoredPosition = Vector2.zero;
            contentTxt.text = msg;
            this.title.text = title;
            this.closeAction = closeAction;
        }
        /// <summary>
        /// 通过URL加载远程文本
        /// </summary>
        /// <param name="url">要显示内容的链接</param>
        /// <param name="closeAction">关闭回调</param>
        public void OpenByURL(string url, string title = "", Action closeAction = null)
        {
            CoroutineUtility.LoadText(url, (str) =>
            {
                Open(str, title, closeAction);
            });
        }
    }
    public class ImageContent : MediaWindow
    {
        private Image viewImage;

        public ImageContent(Transform transform) : base(transform)
        {
            viewImage = mainContent.Find("ViewImage").GetComponent<Image>();
        }
        public override void Update()
        {
            base.Update();
        }
        /// <summary>
        /// 打开窗口显示文本内容
        /// </summary>
        /// <param name="msg">要显示的内容</param>
        /// <param name="closeAction">关闭回调</param>
        public void Open(Sprite sprite, string title = "", Action closeAction = null)
        {
            winRect.gameObject.SetActive(true);
            winRect.anchoredPosition = Vector2.zero;
            viewImage.sprite = sprite;
            this.title.text = title;
            this.closeAction = closeAction;
            Refreshsize();
        }
        /// <summary>
        /// 通过URL加载远程文本
        /// </summary>
        /// <param name="url">要显示内容的链接</param>
        /// <param name="closeAction">关闭回调</param>
        public void OpenByURL(string url, string title = "", Action closeAction = null)
        {
            CoroutineUtility.LoadTexture(url, (tex) =>
            {
                Sprite sprite = Sprite.Create((Texture2D)tex, new Rect(0, 0, tex.width, tex.height), Vector2.one * 0.5f);
                Open(sprite, title, closeAction);
            });
        }

        protected override void OnToggleSize(bool arg)
        {
            base.OnToggleSize(arg);
            Refreshsize();
        }
        private void Refreshsize()
        {
            if (viewImage.sprite == null) return;
            Vector2 winSize = mainContent.rect.size;
            Texture tex = viewImage.sprite.texture;
            RectTransform viewRect = viewImage.GetComponent<RectTransform>();
            if (winSize.x / winSize.y > tex.width / tex.height)
            {
                viewRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, winSize.y / tex.height * tex.width);
                viewRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, winSize.y);
            }
            else
            {
                viewRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, winSize.x);
                viewRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, winSize.x / tex.width * tex.height);
            }
        }
    }
    public class VideoContent : MediaWindow
    {
        private RawImage rawImage;
        private ButtonToggle playToggle;
        private Slider volumeValue;
        private Slider lengthValue;
        private Text lengthText;
        private VideoPlayer videoPlayer;
        RenderTexture render;

        public VideoContent(Transform transform) : base(transform)
        {
            // 初始化字段
            rawImage = mainContent.GetComponent<RawImage>();
            playToggle = downBar.Find("PlayToggle").GetComponent<ButtonToggle>();
            volumeValue = downBar.Find("VolumeValue").GetComponent<Slider>();
            lengthValue = downBar.Find("LengthValue").GetComponent<Slider>();
            lengthText = downBar.Find("LengthText").GetComponent<Text>();
            // 初始化视频纹理
            videoPlayer = mainContent.GetComponent<VideoPlayer>();
            render = new RenderTexture(1920, 1080, 8, RenderTextureFormat.ARGB32);
            render.Create();
            videoPlayer.targetTexture = render;
            rawImage.texture = render;
            // 事件绑定
            playToggle.onClick.AddListener(OnPlayToggle);
            videoPlayer.loopPointReached += VideoPlayer_loopPointReached;
            EventTrigger trigger = null;
            if (!lengthValue.TryGetComponent<EventTrigger>(out trigger))
            {
                trigger = lengthValue.gameObject.AddComponent<EventTrigger>();
            }
            EventTrigger.Entry beginDrag = new EventTrigger.Entry();
            beginDrag.eventID = EventTriggerType.BeginDrag;
            beginDrag.callback.AddListener(OnBeginLength);
            trigger.triggers.Add(beginDrag);
            EventTrigger.Entry endDrag = new EventTrigger.Entry();
            endDrag.eventID = EventTriggerType.EndDrag;
            endDrag.callback.AddListener(OnEndLength);
            trigger.triggers.Add(endDrag);
        }
        /// <summary>
        /// 视频播放至最后
        /// </summary>
        /// <param name="source"></param>
        private void VideoPlayer_loopPointReached(VideoPlayer source)
        {
            playToggle.IsOn = false;
        }

        bool isDraging;
        private void OnBeginLength(BaseEventData arg0)
        {
            //PointerEventData pointer = (PointerEventData)arg0;
            isDraging = true;
        }
        private void OnEndLength(BaseEventData arg0)
        {
            //PointerEventData pointer = (PointerEventData)arg0;
            videoPlayer.time = lengthValue.value;
            isDraging = false;
        }

        public override void Update()
        {
            base.Update();
            lengthValue.maxValue = (float)videoPlayer.length;
            lengthText.text = $"{TimeSpan.FromSeconds((long)videoPlayer.time)}/{TimeSpan.FromSeconds((long)videoPlayer.length)}";
            for (ushort i = 0; i < videoPlayer.controlledAudioTrackCount; i++)
            {
                videoPlayer.SetDirectAudioVolume(i, volumeValue.value);
            }
            if (!isDraging)
            {
                lengthValue.value = (float)videoPlayer.time;
            }
        }
        private void OnPlayToggle(bool arg)
        {
            if (playToggle.IsOn)
            {
                videoPlayer.Play();
            }
            else
            {
                videoPlayer.Pause();
            }
        }
        private void PlayInit()
        {
            volumeValue.value = 0.5f;
            lengthValue.value = 0;
            playToggle.IsOn = true;
            isDraging = false;
            videoPlayer.Play();
        }
        /// <summary>
        /// 打开窗口并播放视频
        /// </summary>
        /// <param name="msg">视频剪辑</param>
        /// <param name="closeAction">关闭回调</param>
        public void Open(VideoClip clip, string title = "", Action closeAction = null)
        {
            winRect.gameObject.SetActive(true);
            winRect.anchoredPosition = Vector2.zero;
            videoPlayer.clip = clip;
            this.title.text = title;
            this.closeAction = closeAction;
            PlayInit();
        }
        /// <summary>
        /// 打开窗口并播放视频
        /// </summary>
        /// <param name="url">视频地址</param>
        /// <param name="closeAction">关闭回调</param>
        public void OpenByURL(string url, string title = "", Action closeAction = null)
        {
            winRect.gameObject.SetActive(true);
            winRect.anchoredPosition = Vector2.zero;
            videoPlayer.url = url;
            this.title.text = title;
            this.closeAction = closeAction;
            PlayInit();
        }
    }
    public class AudioContent : MediaWindow
    {
        private ButtonToggle playToggle;
        private Slider volumeValue;
        private Slider lengthValue;
        private Text lengthText;
        private AudioSource audioEngine;

        public AudioContent(Transform transform) : base(transform)
        {
            // 初始化字段
            playToggle = downBar.Find("PlayToggle").GetComponent<ButtonToggle>();
            volumeValue = downBar.Find("VolumeValue").GetComponent<Slider>();
            lengthValue = downBar.Find("LengthValue").GetComponent<Slider>();
            lengthText = downBar.Find("LengthText").GetComponent<Text>();
            audioEngine = mainContent.GetComponent<AudioSource>();

            // 事件绑定
            playToggle.onClick.AddListener(OnPlayToggle);
            EventTrigger trigger = null;
            if (!lengthValue.TryGetComponent<EventTrigger>(out trigger))
            {
                trigger = lengthValue.gameObject.AddComponent<EventTrigger>();
            }
            EventTrigger.Entry beginDrag = new EventTrigger.Entry();
            beginDrag.eventID = EventTriggerType.BeginDrag;
            beginDrag.callback.AddListener(OnBeginLength);
            trigger.triggers.Add(beginDrag);
            EventTrigger.Entry endDrag = new EventTrigger.Entry();
            endDrag.eventID = EventTriggerType.EndDrag;
            endDrag.callback.AddListener(OnEndLength);
            trigger.triggers.Add(endDrag);
        }
        bool isDraging;
        private void OnBeginLength(BaseEventData arg0)
        {
            //PointerEventData pointer = (PointerEventData)arg0;
            isDraging = true;
        }
        private void OnEndLength(BaseEventData arg0)
        {
            //PointerEventData pointer = (PointerEventData)arg0;
            audioEngine.time = lengthValue.value;
            isDraging = false;
        }

        public override void Update()
        {
            base.Update();
            if (audioEngine.clip == null)
            {
                return;
            }
            audioEngine.volume = volumeValue.value;
            lengthValue.maxValue = audioEngine.clip.length;
            lengthText.text = $"{TimeSpan.FromSeconds((long)audioEngine.time)}/{TimeSpan.FromSeconds((long)audioEngine.clip.length)}";
            if (!isDraging)
            {
                lengthValue.value = (float)audioEngine.time;
            }
            if (!audioEngine.isPlaying && audioEngine.time > audioEngine.clip.length - 0.1f)
            {
                audioEngine.time = 0;
                audioEngine.Pause();
                playToggle.IsOn = false;
            }
        }
        private void OnPlayToggle(bool arg)
        {
            if (playToggle.IsOn)
            {
                audioEngine.Play();
            }
            else
            {
                audioEngine.Pause();
            }
        }
        private void PlayInit()
        {
            volumeValue.value = 0.5f;
            lengthValue.value = 0;
            playToggle.IsOn = true;
            isDraging = false;
            audioEngine.Play();
        }
        /// <summary>
        /// 打开窗口并播放视频
        /// </summary>
        /// <param name="msg">视频剪辑</param>
        /// <param name="closeAction">关闭回调</param>
        public void Open(AudioClip clip, string title = "", Action closeAction = null)
        {
            winRect.gameObject.SetActive(true);
            winRect.anchoredPosition = Vector3.zero;
            audioEngine.clip = clip;
            this.title.text = title;
            this.closeAction = closeAction;
            PlayInit();
        }
        /// <summary>
        /// 打开窗口并播放视频
        /// </summary>
        /// <param name="url">视频地址</param>
        /// <param name="closeAction">关闭回调</param>
        public void OpenByURL(string url, string title = "", Action closeAction = null)
        {
            CoroutineUtility.LoadAudio(url, AudioType.MPEG, (clip) =>
            {
                Open(clip, title, closeAction);
            });
        }
    }
    public class ModelContent : MediaWindow
    {
        Button resetBtn;
        ButtonToggle playToggle;
        Slider scaleValue;
        RawImage preview;
        Camera camera;
        RectTransform model;

        public ModelContent(Transform transform) : base(transform)
        {
            // 字段初始化
            resetBtn = downBar.Find("ResetBtn").GetComponent<Button>();
            playToggle = downBar.Find("PlayToggle").GetComponent<ButtonToggle>();
            scaleValue = downBar.Find("ScaleValue").GetComponent<Slider>();
            preview = mainContent.Find("Preview").GetComponent<RawImage>();
            camera = mainContent.Find("Camera").GetComponent<Camera>();
            model = mainContent.Find("Model").GetComponent<RectTransform>();
            // 渲染
            RenderTexture render = new RenderTexture(1920, 1080, 8, RenderTextureFormat.ARGB32);
            render.Create();
            camera.targetTexture = render;
            preview.texture = render;
            // 事件绑定
            resetBtn.onClick.AddListener(OnResetClick);
            scaleValue.onValueChanged.AddListener(OnScaleChange);
            EventTrigger trigger = null;
            if (!preview.TryGetComponent<EventTrigger>(out trigger))
            {
                trigger = preview.gameObject.AddComponent<EventTrigger>();
            }
            EventTrigger.Entry begin = new EventTrigger.Entry();
            begin.eventID = EventTriggerType.BeginDrag;
            begin.callback.AddListener(OnBeginDrag);
            trigger.triggers.Add(begin);

            EventTrigger.Entry dragging = new EventTrigger.Entry();
            dragging.eventID = EventTriggerType.Drag;
            dragging.callback.AddListener(OnDrag);
            trigger.triggers.Add(dragging);
        }

        private void OnScaleChange(float arg0)
        {
            model.localScale = Vector3.one * arg0;
        }

        private void OnBeginDrag(BaseEventData arg)
        {
            PointerEventData pointer = arg as PointerEventData;
            playToggle.IsOn = false;
        }
        private void OnDrag(BaseEventData arg)
        {
            PointerEventData pointer = arg as PointerEventData;
            Vector2 delta = pointer.delta;
            if (target)
            {
                target.transform.Rotate(0, -delta.x, 0, Space.World);
            }
            model.Rotate(delta.y, 0, 0, Space.Self);
        }
        private void OnResetClick()
        {
            model.localRotation = Quaternion.identity;
            scaleValue.value = (scaleValue.minValue + scaleValue.maxValue) / 2;
            if (target)
            {
                playToggle.IsOn = false;
                target.transform.localRotation = Quaternion.identity;
                target.transform.localPosition = Vector3.zero;
            }
        }

        public override void Update()
        {
            base.Update();
            if (playToggle.IsOn && target)
            {
                target.transform.Rotate(0, Time.deltaTime * 100, 0, Space.World);
            }
        }
        GameObject target;
        /// <summary>
        /// 打开窗口显示模型
        /// </summary>
        /// <param name="msg">要显示的内容</param>
        /// <param name="closeAction">关闭回调</param>
        public void Open(GameObject target, string title = "", Action closeAction = null)
        {
            this.title.text = title;
            this.closeAction = closeAction;
            for (int i = 0; i < model.childCount; i++)
            {
                GameObject.Destroy(model.GetChild(i).gameObject);
            }
            winRect.gameObject.SetActive(true);
            winRect.anchoredPosition = Vector2.zero;
            GameObject go = GameObject.Instantiate(target, model);
            Bounds bounds = Utillity.GetRendererBounds(go.transform);
            Vector3 size = bounds.size;
            float length = size.x < size.y ? size.y : size.x;
            length = length < size.z ? size.z : length;
            float rate = 1.0f / length;
            go.transform.localScale = Vector3.one * rate;
            this.target = go;
            OnResetClick();
        }
    }
}
